package cn.coufran.springboot.starter.http;

import cn.coufran.commons.Result;
import cn.coufran.doorgod.Checker;
import cn.coufran.springboot.starter.http.content.BinaryContent;
import cn.coufran.springboot.starter.http.content.HttpResponseBodyContent;
import cn.coufran.springboot.starter.http.content.TextContent;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;

import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;

import static cn.coufran.doorgod.decider.Deciders.is;

/**
 * HTTP响应
 * @author Coufran
 * @version 2.0.0
 * @since 1.0.0
 */
public class HttpResponse {
    private static final List<MediaType> MEDIA_TYPE_TEXT = Arrays.asList(
            MediaType.TEXT__PLAIN,
            MediaType.TEXT__HTML,
            MediaType.TEXT__XML,
            MediaType.TEXT__MARKDOWN,
            MediaType.APPLICATION__JSON,
            MediaType.APPLICATION__XML
    );

    /** 状态码 */
    private int statusCode;
    /** 响应类型 */
    private ContentType contentType;
    /** 响应体 */
    private HttpResponseBodyContent content;

    /**
     * 构造方法
     * @param statusCode 状态码
     * @param contentType 响应类型
     * @param content 响应体
     */
    public HttpResponse(int statusCode, ContentType contentType, HttpResponseBodyContent content) {
        this.statusCode = statusCode;
        this.contentType = contentType;
        this.content = content;
    }

    /**
     * 构造方法
     * @param statusCode 状态码
     * @param contentType 响应类型
     * @param content 响应体
     */
    public HttpResponse(int statusCode, ContentType contentType, byte[] content) {
        this.statusCode = statusCode;
        this.contentType = contentType;

        if (isStringContent(contentType)) {
            this.content = new TextContent(new String(content, StandardCharsets.UTF_8));
        } else {
            this.content = new BinaryContent(content);
        }

    }

    /**
     * 获取状态码
     * @return 状态码
     */
    public int statusCode() {
        return statusCode;
    }

    /**
     * 校验HTTP响应
     * @return HTTP响应
     */
    public HttpResponse check() {
        return this.checkStatusCode();
    }

    /**
     * 校验状态码是不是200
     * @return HTTP响应
     */
    public HttpResponse checkStatusCode() {
        return this.checkStatusCode(200);
    }

    /**
     * 校验状态码
     * @param statusCode 状态码
     * @return HTTP响应
     */
    public HttpResponse checkStatusCode(int statusCode) {
        Checker.check(this.statusCode, is(statusCode), "status code is " + this.statusCode + ": " + this.asString());
        return this;
    }

    public ContentType contentType() {
        return this.contentType;
    }

    /**
     * 获取响应体
     * @return 响应体
     */
    public HttpResponseBodyContent content() {
        return this.content;
    }

    /**
     * 获取byte[]类型的响应体
     * @return 响应体
     */
    public byte[] asByteArray() {
        return content().asBytes();
    }

    /**
     * 获取String类型的响应体
     * @return 响应体
     */
    public String asString() {
        return content().asString();
    }

    /**
     * 获取JSON类型的响应体
     * @param <T> JSON类型
     * @return 响应体
     */
    public <T> T asJson() {
        String bodyStr = this.asString();
        if (bodyStr.startsWith("[")) {
            return (T) JSON.parseArray(bodyStr);
        } else {
            return (T) JSON.parseObject(bodyStr);
        }
    }

    /**
     * 获取响应体对象
     * @param <T> 对象类型
     * @param type 对象类型
     * @return 响应体
     */
    public <T> T asObject(Class<T> type) {
        return this.asObject((Type) type);
    }

    /**
     * 获取响应体对象
     * @param <T> 对象类型
     * @param type 对象类型
     * @return 响应体
     */
    public <T> T asObject(TypeReference<T> type) {
        return this.asObject(type.getType());
    }

    /**
     * 获取响应体对象
     * @param <T> 对象类型
     * @param type 对象类型
     * @return 响应体
     */
    public <T> T asObject(Type type) {
        return JSON.parseObject(this.asString(), type);
    }

    /**
     * 获取响应体对象
     * @param <T> Result data类型
     * @return 响应体
     */
    public <T> Result<T> asResult() {
        return this.asResult((Type) Object.class);
    }

    /**
     * 获取响应体对象
     * @param <T> Result data类型
     * @param dataType Result data类型
     * @return 响应体
     */
    public <T> Result<T> asResult(Class<T> dataType) {
        return this.asResult((Type) dataType);
    }

    /**
     * 获取响应体对象
     * @param <T> Result data类型
     * @param dataType Result data类型
     * @return 响应体
     */
    public <T> Result<T> asResult(TypeReference<T> dataType) {
        return this.asResult(dataType.getType());
    }

    /**
     * 获取响应体对象
     * @param <T> Result data类型
     * @param dataType Result data类型
     * @return 响应体
     */
    public <T> Result<T> asResult(Type dataType) {
        TypeReference<Result<T>> type
                = new TypeReference<Result<T>>(dataType) {};
        return asObject(type);
    }

    /**
     * 转换为字符串
     * @return 字符串
     */
    @Override
    public String toString() {
        return "HttpResponse{" +
                "statusCode=" + statusCode +
                ", contentType=" + contentType +
                ", content=" + content +
                '}';
    }

    private boolean isStringContent(ContentType contentType) {
        for (MediaType textMediaType : MEDIA_TYPE_TEXT) {
            if (contentType.getMediaType() == textMediaType) {
                return true;
            }
        }
        return false;
    }
}
